/**
 * C Implementation: grib_precision
 *
 * Description:
 * how to control decimal precision when packing fields.
 * 打包字段时如何控制小数精度。
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

#include "eccodes.h"

#define EPSILON 1e-6
#define EXPECTED_MIN 270.466796875
#define EXPECTED_MAX 311.096796875

int sfx_grib_precision()
{
    int err = 0;

    const char* infile  = "d:/sfxData/ECMWF/eccodes_test_data/regular_latlon_surface.grib1";
    const char* outfile = "d:/temp/out_precision_grib1";

    FILE* in  = fopen(infile, "rb");
    FILE* out = fopen(outfile, "wb");

    // create a new handle from a message in a file
    codes_handle* h = codes_handle_new_from_file(0, in, PRODUCT_GRIB, &err);

    // bitsPerValue before changing the packing parameters (在改变包装参数之前的每个值的位数)
    long bitsPerValue1 = 0;
    CODES_CHECK(codes_get_long(h, "bitsPerValue", &bitsPerValue1), 0);
    assert(bitsPerValue1 == 16);

    // get the size of the values array
    size_t size = 0;
    CODES_CHECK(codes_get_size(h, "values", &size), 0);
    assert(size == 496);

    // get data values before changing the packing parameters
    double* values1 = (double*)malloc(size * sizeof(double));
    CODES_CHECK(codes_get_double_array(h, "values", values1, &size), 0);

    // changing decimal precision to 2 means that 2 decimal digits are preserved when packing.
    long decimalPrecision = 2;
    CODES_CHECK(codes_set_long(h, "changeDecimalPrecision", decimalPrecision), 0);

    // bitsPerValue after changing the packing parameters
    long bitsPerValue2 = 0;
    CODES_CHECK(codes_get_long(h, "bitsPerValue", &bitsPerValue2), 0);
    assert(bitsPerValue2 == 12);

    // get data values after changing the packing parameters
    double* values2 = (double*)malloc(size * sizeof(double));
    CODES_CHECK(codes_get_double_array(h, "values", values2, &size), 0);

    // computing error (计算误差)
    double a    = 0;  // 绝对
    double r    = 0;  // 相对
    double maxa = 0;  // 最大绝对
    double maxr = 0;  // 最大相对
    double maxv = values2[0];
    double minv = maxv;
    for (size_t i = 0; i < size; i++) {
        a = fabs(values2[i] - values1[i]);
        if (values2[i] > maxv) maxv = values2[i];
        if (values2[i] < minv) minv = values2[i];
        if (values2[i] != 0) r = fabs((values2[i] - values1[i]) / values2[i]);
        if (a > maxa) maxa = a;
        if (r > maxr) maxr = r;
    }

    printf("max absolute error = %g\n", maxa);
    printf("max relative error = %g\n", maxr);
    printf("min value = %g\n", minv);
    printf("max value = %g\n", maxv);
    assert(fabs(minv - EXPECTED_MIN) < EPSILON);
    assert(fabs(maxv - EXPECTED_MAX) < EPSILON);

    // get the coded message in a buffer
    const void* buffer = NULL;
    CODES_CHECK(codes_get_message(h, &buffer, &size), 0);

    // write the buffer in a file
    if (fwrite(buffer, 1, size, out) != size) {
        perror(outfile);
    }

    // free memory
    free(values2);
    free(values1);

    codes_handle_delete(h);
    fclose(out);
    fclose(in);
    return 0;
}


